package javapop.framework.input;

import javapop.framework.ParseInput;
import javapop.framework.ParsePosition;

public class StringParseInput implements ParseInput {
	private String input;
	private int pos;
	private int charPos;
	private int linePos;
	private boolean moved;
	
	public StringParseInput(String input) {
		this.input = input;
		pos = 0;
		charPos = 1;
		linePos = 1;
		moved = false;
	}
	
	@Override
	public int getPos() { 
		return pos;
	}
	
	@Override
	public int getLinePos() {
		return linePos;
	}
	
	@Override
	public int getCharPos() {
		if(moved) {
			int count = 0;
			int ppos = pos;
			while(ppos>=0) {
				if(input.charAt(ppos)=='\n') {
					moved = false;
					charPos = ppos;
					return charPos;
				}
				ppos--;
				count++;
			}
			moved = false;
			charPos = count;
			return charPos;
		} else {
			return charPos;
		}
	}
	
	@Override
	public Character fetch() {
		if(pos>=input.length()) {
			return null; // eof 
		} else {
			return input.charAt(pos);
		}
	}
	
	@Override
	public boolean isEOF() {
		return fetch()==null;
	}
	
	@Override
	public Character next() {
		Character ch = fetch();
		if(ch==null) {
			return ch; // eof
		}
		pos++;
		charPos++;
		if(ch.equals('\n')) {
			linePos++;
			charPos=1;
		}
		moved = false;
		return ch;
	}
	
	@Override
	public Character prev() {
		if(pos==0) {
			return null; // eof
		}
		pos--;
		Character ch = fetch();
		if(ch.equals('\n')) {
			linePos--;
		}
		moved = true;
		return ch;
	}

	private void rewind(int nb) {
		for(int i=0;i<nb;i++) {
			if(prev()==null) {
				throw new IllegalArgumentException("Rewind out of buffer");
			}
		}	
	}
	
	private void forward(int nb) {
		for(int i=0;i<nb;i++) {
			if(next()==null) {
				throw new IllegalArgumentException("Forward out of buffer");
			}
		}
	}
	
	@Override
	public void move(int pos) {
		if(this.pos==pos) {
			// do nothing
		} else if(this.pos>pos) {
			rewind(this.pos-pos);
		} else {
			forward(pos-this.pos);
		}
	}
	
	@Override
	public ParsePosition getParsePosition() {
		return new ParsePosition(getPos(),getCharPos(),getLinePos());
	}
	@Override
	public boolean consume(String str) {
		int savepos = pos;
		if(isEOF())
			return false;
		for(int i=0;i<str.length();i++) {
			if(!next().equals(str.charAt(i))) {
				move(savepos);
				return false;
			}
		}
		return true;
	}
	
	@Override
	public boolean consumeChar(Character ch) {
		if(isEOF())
			return false;
		if(fetch().equals(ch)) {
			next();
			return true;
		} else {
			return false;
		}
	}
	
	@Override
	public void skipSpacesStart() {
		while(true) {
			Character ch = fetch();
			if(ch==null || !Character.isWhitespace(ch))
				return;
			next();
		}
	}
	
	@Override
	public void skipSpacesEnd() {
		Character ch = fetch();
		if(ch==null || !Character.isWhitespace(ch))
			return;
		while(true) {
			ch = prev();
			if(ch==null || !Character.isWhitespace(ch))
				return;
		}
	}

	@Override
	public String show(int before, int after, int max) {
		if(before<0) {
			throw new IllegalArgumentException("Cannot show with negative before");
		}
		if(after<0) {
			throw new IllegalArgumentException("Cannot show with negative after");
		}
		int count = 0;
		int npos = pos - before;
		if(npos<0) {
			npos = 0;
		}
		StringBuffer buf = new StringBuffer();
		for(int p=npos;p<pos;p++) {
			buf.append(input.charAt(p));
			count++;
			if(count>=max) {
				return buf.toString();
			}
		}
		buf.append("_");
		for(int p=pos;p<pos+after;p++) {
			if(p>=input.length()) {
				return buf.toString();
			}
			Character ch = input.charAt(p);
			if(ch.equals('\n')) {
				return buf.toString();
			}
			buf.append(ch);
			count++;
			if(count>=max) {
				return buf.toString();
			}			
		}
		return buf.toString();		
	}
	
	@Override
	public String show(int before, int after) {
		return show(before,after,80);
	}
	
	@Override
	public String thisLine() {
		StringBuilder buf = new StringBuilder();
		int p = pos;
		while(true) {
			if(p==input.length()) {
				return buf.toString();
			}
			char ch = input.charAt(p);
			if(ch=='\n') {
				return buf.toString();
			}

			buf.append(ch);
			p++;
		}
	}
	
	@Override
	public String extract(int startPos, int endPos) {
		StringBuilder buf = new StringBuilder();
		for(int p=pos;p<input.length();p++) {
			buf.append(input.charAt(p));			
		}
		return buf.toString();
	}
	
	@Override
	public String toString() {
		StringBuffer buf = new StringBuffer();
		for(int p=0;p<input.length();p++) {
			if(p==pos) {
				buf.append("_");
			}
			buf.append(input.charAt(p));
		}
		return buf.toString();		
	}
}
